/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/tbox/PIO.h"

//COMMON headers
#include "COMMON/base/Common.h"
#include "COMMON/base/Constants.h"
#include "COMMON/tbox/ParallelBuffer.h"
#include "COMMON/tbox/Utilities.h"

namespace DOGOS
{

int PIO::s_rank = -1;
std::ofstream * PIO::s_filestream = 0;

/*
 *************************************************************************
 *
 * Define the parallel buffers and the associated ostream objects.
 *
 *************************************************************************
 */

static ParallelBuffer pout_buffer;
static ParallelBuffer perr_buffer;
static ParallelBuffer plog_buffer;

std::ostream POUT(&pout_buffer);
std::ostream PERR(&perr_buffer);
std::ostream PLOG(&plog_buffer);

/*
 *************************************************************************
 *
 * Initialize the parallel I/O streams.  This routine must be called
 * before POUT, PERR, and PLOG are used for output but after SAMRAI_MPI
 * has been initialized.  By default, logging is disabled.
 *
 *************************************************************************
 */

void
PIO::initialize()
{
    s_rank = 0;
    s_filestream = 0;

    /*
     * Initialize the standard parallel output stream
     */

    pout_buffer.set_active(s_rank == 0);
    pout_buffer.set_prefix_string(std::string());
    pout_buffer.set_output_stream1(&std::cout);
    pout_buffer.set_output_stream2(0);

    /*
     * Initialize the error parallel output stream
     */

    std::string buffer = "P=" + Utilities::int_to_string(s_rank, TBOX_PROCESSOR_WIDTH) + ":";

    perr_buffer.set_active(true);
    perr_buffer.set_prefix_string(buffer);
    perr_buffer.set_output_stream1(&std::cerr);
    perr_buffer.set_output_stream2(0);

    /*
     * Initialize the parallel log file (disabled by default)
     */

    plog_buffer.set_active(false);
    plog_buffer.set_prefix_string(std::string());
    plog_buffer.set_output_stream1(0);
    plog_buffer.set_output_stream2(0);
}

/*
 *************************************************************************
 *
 * Close the output streams.  Flush both cout and cerr.  If logging,
 * then flush and close the log stream.
 *
 *************************************************************************
 */

void
PIO::finalize()
{
    std::cout.flush();
    std::cerr.flush();
    shutdown_filestream();
}

/*
 *************************************************************************
 *
 * If the log file stream is open, then shut down the filestream.  Close
 * and flush the channel and disconnect the output stream buffers.
 *
 *************************************************************************
 */

void
PIO::shutdown_filestream()
{
    if (s_filestream)
    {
        s_filestream->flush();
        s_filestream->close();

        delete s_filestream;
        s_filestream = 0;

        pout_buffer.set_output_stream2(0);
        perr_buffer.set_output_stream2(0);
        plog_buffer.set_output_stream1(0);
        plog_buffer.set_active(false);
    }
}

/*
 *************************************************************************
 *
 * Log messages for node zero only.  If a log stream was open, close
 * it.  If this is node zero, then open a new log stream and set the
 * appropriate buffer streams to point to the log file.
 *
 *************************************************************************
 */

void
PIO::log_only_node_zero(
    const std::string& file_name)
{
    /*
     * If the filestream was open, then close it and reset streams
     */

    shutdown_filestream();

    /*
     * If this is node zero, then open the log stream and redirect output
     */

    if (s_rank == 0)
    {
        s_filestream = new std::ofstream(file_name.c_str());
        if (!(*s_filestream))
        {
            delete s_filestream;
            s_filestream = 0;
            PERR << "PIO: Could not open log file ``" << file_name.c_str()
            << "''\n";
        }
        else
        {
            pout_buffer.set_output_stream2(s_filestream);
            perr_buffer.set_output_stream2(s_filestream);
            plog_buffer.set_output_stream1(s_filestream);
            plog_buffer.set_active(true);
        }
    }
}

/*
 *************************************************************************
 *
 * Log messages for all nodes.  If a log stream was open, the close it.
 * Open a log stream on every processor.  The file_name for the log file
 * will be appended with the processor number.
 *
 *************************************************************************
 */

void
PIO::log_all_nodes(
    const std::string& file_name)
{
    /*
     * If the filestream was open, then close it and reset streams
     */

    shutdown_filestream();

    /*
     * Open the log stream and redirect output
     */

    std::string full_file_name = file_name + "."
                                + Utilities::int_to_string(s_rank, TBOX_PROCESSOR_WIDTH);
    s_filestream = new std::ofstream(full_file_name.c_str());

    if (!(*s_filestream))
    {
        delete s_filestream;
        s_filestream = 0;
        PERR << "PIO: Could not open log file ``" << full_file_name << "''\n";
    }
    else
    {
        pout_buffer.set_output_stream2(s_filestream);
        perr_buffer.set_output_stream2(s_filestream);
        plog_buffer.set_output_stream1(s_filestream);
        plog_buffer.set_active(true);
    }
}

/*
 *************************************************************************
 *
 * Suspend logging of data to the file stream.  This does not close the
 * filestream (assuming it is open) but just disables logging.
 *
 *************************************************************************
 */
void
PIO::suspend_logging()
{
    pout_buffer.set_output_stream2(0);
    perr_buffer.set_output_stream2(0);
    plog_buffer.set_output_stream1(0);
    plog_buffer.set_active(false);
}

/*
 *************************************************************************
 *
 * Resume logging of the file stream (assuming it was open).  If the
 * file stream is 0, then do nothing.
 *
 *************************************************************************
 */
void
PIO::resume_logging()
{
    if (s_filestream)
    {
        pout_buffer.set_output_stream2(s_filestream);
        perr_buffer.set_output_stream2(s_filestream);
        plog_buffer.set_output_stream1(s_filestream);
        plog_buffer.set_active(true);
    }
}

}
